Skip to content

Static invoice server #3628

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 20 commits into
base: main
Choose a base branch
from

Conversation

valentinewallace
Copy link
Contributor

As part of supporting async payments, we want to support serving static invoices on behalf of an often-offline payee, in response to invoice requests from payers.

See this doc for more details on the protocol. Here we implement the server-side of the linked protocol.

Based on #3618

shaavan and others added 20 commits May 27, 2025 16:34
Document that MessageForwardNode must represent a node that supports
the onion messages feature in order to be used in blinded reply paths.
Encapsulates logic for fetching peers used in blinded path creation.
Reduces duplication and improves reusability across functions.
`OffersMessageFlow` is a mid-level abstraction for handling
BOLT12 messages and flow control. It provides utilities to
help implement Offer Message Handlers in a cleaner, more modular
way.

The core motivation is to decouple Onion Messaging logic from
`ChannelManager`, reducing its responsibilities and code overhead.
This separation improves clarity, maintainability, and lays the
groundwork for giving users greater flexibility in customizing
their BOLT12 message flows.
These functions will be used in the following commit to replace closure usage
in Flow trait functions.
As part of being an async recipient, we need to support interactively building
an offer and static invoice with an always-online node that will serve static
invoices on our behalf.

Add a config field containing blinded message paths that async recipients can
use to request blinded paths that will be included in their offer. Payers will
forward invoice requests over the paths returned by the server, and receive a
static invoice in response if the recipient is offline.
Because async recipients are not online to respond to invoice requests,
the plan is for another node on the network that is always-online to serve
static invoices on their behalf.

The protocol is as follows:
- Recipient is configured with blinded message paths to reach the static invoice
  server
- On startup, recipient requests blinded message paths for inclusion in their
  offer from the static invoice server over the configured paths
- Server replies with offer paths for the recipient
- Recipient builds their offer using these paths and the corresponding static
  invoice and replies with the invoice
- Server persists the invoice and confirms that they've persisted it, causing
  the recipient to cache the interactively built offer for use

At pay-time, the payer sends an invoice request to the static invoice server,
who replies with the static invoice after forwarding the invreq to the
recipient (to give them a chance to provide a fresh invoice in case they're
online).

Here we add the requisite trait methods and onion messages to support this
protocol.
In future commits, as part of being an async recipient, we will interactively
build offers and static invoices with an always-online node that will serve
static invoices on our behalf.

Once an offer is built and the static invoice is confirmed as persisted by the
server, we will use the new offer cache added here to save the invoice metadata
and the offer in ChannelManager, though the OffersMessageFlow is responsible
for keeping the cache updated.
As an async recipient, we need to interactively build static invoices that an
always-online node will serve to payers on our behalf.

At the start of this process, we send a requests for paths to include in our
offers to the always-online node on startup and refresh the cached offers when
they expire.
As an async recipient, we need to interactively build a static invoice that an
always-online node will serve to payers on our behalf.

As part of this process, the static invoice server sends us blinded message
paths to include in our offer so they'll receive invoice requests from senders
trying to pay us while we're offline. On receipt of these paths, create an
offer and static invoice and send the invoice back to the server so they can
provide the invoice to payers.
As an async recipient, we need to interactively build a static invoice that an
always-online node will serve on our behalf.

Once this invoice is built and persisted by the static invoice server, they
will send us a confirmation onion message. At this time, cache the
corresponding offer and mark it as ready to receive async payments.
As an async recipient, we need to interactively build offers and corresponding
static invoices, the latter of which an always-online node will serve to payers
on our behalf.

Offers may be very long-lived and have a longer expiration than their
corresponding static invoice. Therefore, persist a fresh invoice with the
static invoice server when the current invoice gets close to expiration.
Over the past several commits we've implemented interactively building an async
receive offer with a static invoice server that will service invoice requests
on our behalf as an async recipient.

Here we add an API to retrieve the resulting offers so we can receive payments
when we're offline.
In upcoming commits, we need to check whether a static invoice or its underlying
offer is expired in no-std builds. Here we expose the methods to do so. The
methods could instead be kept private to the crate, but they seem potentially
useful.
As part of serving static invoices to payers on behalf of often-offline
recipients, these recipients need a way to contact the static invoice server to
retrieve blinded paths to include in their offers.

Add a utility to create blinded paths for this purpose as a static invoice
server. The recipient will be configured with the resulting paths and use them
to request offer paths on startup.
As part of serving static invoices to payers on behalf of often-offline
recipients, we need to provide the async recipient with blinded message paths
to include in their offers.

Support responding to inbound requests for offer paths from async recipients.
As part of serving static invoices to payers on behalf of often-offline
recipients, the recipient will send us the final static invoice once it's done
being interactively built. We will then persist this invoice and confirm to
them that the corresponding offer is ready to be used for async payments.

Surface an event once the invoice is received and expose an API to tell the
recipient that it's ready for payments.
Here we implement serving static invoices to payers on behalf of often-offline
recipients. These recipients previously encoded blinded paths terminating at
our node in their offer, so we receive invoice requests on their behalf.

Handle those inbound invreqs by retrieving a static invoice we previously
persisted on behalf of the payee, and forward it to the payer as a reply to
their invreq.
We were manually creating the static invoice in tests, but now we can use the
static invoice server protocol to interactively build the invoice.
@valentinewallace valentinewallace force-pushed the 2025-02-static-invoice-server branch from 1c6073b to 4a287ea Compare May 29, 2025 16:58
Copy link

codecov bot commented May 29, 2025

Codecov Report

Attention: Patch coverage is 72.37852% with 216 lines in your changes missing coverage. Please review.

Project coverage is 89.63%. Comparing base (101aa6f) to head (4a287ea).
Report is 7 commits behind head on main.

Files with missing lines Patch % Lines
lightning/src/ln/channelmanager.rs 42.13% 90 Missing and 2 partials ⚠️
lightning/src/onion_message/async_payments.rs 0.00% 29 Missing ⚠️
lightning/src/onion_message/functional_tests.rs 0.00% 20 Missing ⚠️
lightning/src/offers/flow.rs 96.52% 11 Missing and 6 partials ⚠️
lightning/src/ln/peer_handler.rs 0.00% 15 Missing ⚠️
lightning/src/offers/async_receive_offer_cache.rs 31.81% 15 Missing ⚠️
lightning/src/util/test_utils.rs 43.47% 7 Missing and 6 partials ⚠️
lightning/src/offers/static_invoice.rs 0.00% 9 Missing ⚠️
lightning/src/offers/invoice.rs 0.00% 3 Missing ⚠️
lightning/src/offers/invoice_macros.rs 0.00% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3628      +/-   ##
==========================================
- Coverage   89.76%   89.63%   -0.14%     
==========================================
  Files         159      161       +2     
  Lines      128828   129349     +521     
  Branches   128828   129349     +521     
==========================================
+ Hits       115644   115939     +295     
- Misses      10503    10698     +195     
- Partials     2681     2712      +31     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@valentinewallace valentinewallace mentioned this pull request Feb 27, 2025
31 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants